/*
 * topological-hex, a program to compute combinatorial hexahedral meshes.
 *
 * Copyright (C) <2018> <Université catholique de Louvain (UCL), Belgique>
 *
 * List of the contributors to the development, description and complete
 * License: see LICENSE file.
 *
 * This program (topological-hex) is free software:
 * you can redistribute it and/or modify it under the terms
 * of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see COPYING file).  If not,
 * see <http://www.gnu.org/licenses/>.
 */

#ifndef UTILS_H_
#define UTILS_H_

#include <stdint.h>

/**
 * @defgroup utils Utilities
 *
 * Contains compiler-specific definitions and macros.
 */

/**
 * @addtogroup utils
 * @{
 */

#define ALIGNMENT 32

#ifdef __GNUC__

#define ALIGNED __attribute__((aligned(ALIGNMENT)))
#define ALIGNED16 __attribute__((aligned(16)))

#define ASSUME_ALIGNED(p) __builtin_assume_aligned((p), ALIGNMENT)

#else

#define ALIGNED
#define ALIGNED16

#define ASSUME_ALIGNED(p) (p)

#endif

#ifdef __GNUC__

/**
 * Returns the index of the least-significant bit set to 1 in a 64-bit
 * integer. The result may be undefined if i is equal to 0.
 */
static inline
uint32_t find_lsb64(uint64_t i) { return __builtin_ctzll(i); }

#else

static inline
uint32_t find_lsb64(uint64_t i) {
  uint32_t j;
  for (j = 0; j < 64 && (i & (1ull << j)) == 0; j++);
  return j;
}

#endif

#ifdef __GNUC__

/**
 * Returns the index of the least-significant bit set to 1 in a 32-bit
 * integer. The result may be undefined if i is equal to 0.
 */
static inline
uint32_t find_lsb32(uint32_t i) {
  return __builtin_ctz(i);
}

#else

static inline
uint32_t find_lsb32(uint32_t i) {
  uint32_t j;
  for (j = 0; j < 32 && (i & (1ul << j)) == 0; j++);
  return j;
}

#endif

#ifdef __GNUC__

/**
 * Counts the number of bits set to 1 in a 64-bit integer.
 */
static inline
uint64_t popcount64(uint64_t i) { return __builtin_popcountll(i); }

#else

static inline
uint64_t popcount64(uint64_t i) {
  uint32_t n = 0;

  for (uint32_t j = 0; j < 64; j++) {
    if (i & (1ull << j)) n++;
  }

  return n;
}

#endif

#ifdef __GNUC__

/**
 * Returns 1 if the number of bits set to 1 in a number is odd.
 */
static inline
uint64_t parity64(uint64_t i) { return __builtin_parityll(i); }

#else

static inline
uint64_t parity64(uint64_t i) { return popcount64(i) % 2; }

#endif

#define XTOKEN_PASTE(a, b) a##b

/**
 * Expands and concatenates two tokens.
 */
#define TOKEN_PASTE(a, b) XTOKEN_PASTE(a, b)

/**
 * @}
 */

#endif
